package com.myTest.sqlSession;

import com.myTest.config.BoundSql;
import com.myTest.pojo.Configuration;
import com.myTest.pojo.MappedStatement;
import com.myTest.utils.GenericTokenParser;
import com.myTest.utils.ParameterMapping;
import com.myTest.utils.ParameterMappingTokenHandler;
import com.myTest.utils.TokenHandler;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor{

    //查询数据库操作
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... param) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
        //1、注册驱动，获取连接
        //2、获取Sql语句 转换sql语句：把#{}转成?占位符，并存储值
        //3、获取预处理对象preparedStatement,,,解析后的sql语句和参数集合
        //4、参数设置
        PreparedStatement preparedStatement = getPreparedStatement(configuration, mappedStatement, param[0]);

        //5、执行sql
        ResultSet resultSet = preparedStatement.executeQuery();

        //6、封装结果集
        String resultType = mappedStatement.getResultType();
        //获取返回对象类值
        Class<?> resultTypeClass = getClassType(resultType);

        //遍历结果集
        List<Object> objects = new ArrayList<>();
        while (resultSet.next()){
            ResultSetMetaData metaData = resultSet.getMetaData();
            Object o = resultTypeClass.newInstance();
            //遍历元数据
            for (int i=1;i<=metaData.getColumnCount();i++){
                //字段名
                String columnName = metaData.getColumnName(i);
                //字段值
                Object value = resultSet.getObject(columnName);
                //使用反射或内省，根据数据表和实体的对应关系，完成封装
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                //获取读写方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                //觉醒读写方法
                writeMethod.invoke(o,value);
            }
            objects.add(o);
        }
        return (List<E>) objects;
    }

    //更新数据库操作：添加、删除、更新
    @Override
    public int update(Configuration configuration, MappedStatement mappedStatement, Object... param) throws ClassNotFoundException, SQLException, IllegalAccessException, NoSuchFieldException {
        //1、注册驱动，获取连接
        //2、获取Sql语句 转换sql语句：把#{}转成?占位符，并存储值
        //3、获取预处理对象preparedStatement,,,解析后的sql语句和参数集合
        //4、参数设置
        PreparedStatement preparedStatement = getPreparedStatement(configuration, mappedStatement, param[0]);
        //5、执行sql
        preparedStatement.executeUpdate();
        //6、返回更新的条数
        return preparedStatement.getUpdateCount();
    }

    /**
     * 准备预处理对象操作：
     * 1、注册驱动，获取连接
     * 2、获取Sql语句 转换sql语句：把#{}转成?占位符，并存储值
     * 3、获取预处理对象preparedStatement,,,解析后的sql语句和参数集合
     * 4、参数设置
     *
     * @param configuration
     * @param mappedStatement
     * @param obj
     * @return
     * @throws Exception
     */
    private PreparedStatement getPreparedStatement(Configuration configuration, MappedStatement mappedStatement, Object obj) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //1、注册驱动，获取连接
        Connection connection = configuration.getDataSource().getConnection();
        //2、获取sql语句
        //转换sql语句：把#{}转成?占位符，并存储值
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);

        //3、获取预处理对象preparedStatement,,,解析后的sql语句和参数集合
        String sqlText = boundSql.getSqlText();
        PreparedStatement preparedStatement = connection.prepareStatement(sqlText);

        //4、参数设置

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        //获取参数全路径
        String paramterType = mappedStatement.getParamterType();
        Class<?> paramterTypeClass = getClassType(paramterType);
        for (int i=0;i<parameterMappingList.size();i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String content = parameterMapping.getContent();
            Object paramTypeClass;
            if(paramterTypeClass==Integer.class){
                paramTypeClass = obj;
            }else{
                //反射获取属性对象
                Field declaredField = paramterTypeClass.getDeclaredField(content);
                //设置暴力访问
                declaredField.setAccessible(true);
                //参数的值
                paramTypeClass = declaredField.get(obj);
            }
            //给占位符赋值
            preparedStatement.setObject(i+1,paramTypeClass);
        }
        return preparedStatement;
    }

    private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
        if(paramterType!=null){
            Class<?> aClass = Class.forName(paramterType);
            return aClass;
        }
        return null;
    }

    /**
     * 完成对#{}的解析工作，1、将#{}使用?进行代替，2解析出#{}里的值进行存储
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //标记处理类：主要是配合通⽤标记解析器GenericTokenParser类完成对配置⽂件等的解析⼯作，其中TokenHandler主要完成处理
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        //GenericTokenParser :通⽤的标记解析器，完成了代码⽚段中的占位符的解析，然后再根据给定的标记处理器(TokenHandler)来进⾏表达式的处理
        //三个参数：分别为openToken (开始标记)、closeToken (结束标记)、handler (标记处 理器)
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        String parseSql = genericTokenParser.parse(sql);
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
        return boundSql;
    }
}
